package com.own.component.store.core.util;

import com.own.component.common.exception.BusinessSimpleException;
import com.own.component.store.core.util.base.BaseLockMethod;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.function.Supplier;

/**
 * LockStoreUtil
 *
 * @author chenxueli
 * @date 2023-07-21 11:15:38
 */
@Slf4j
public class LockStoreUtil {

    private final BaseLockMethod baseLockMethod;
    /**
     * 值的map
     */
    private final Map<String, Object> VALUE_MAP = new HashMap<>();

    public LockStoreUtil(BaseLockMethod baseLockMethod) {
        this.baseLockMethod = baseLockMethod;
    }

    /**
     * 睡眠操作
     *
     * @param millis 睡眠时间
     */
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取锁对象
     *
     * @param key 锁的key
     * @return 锁
     */
    public Lock getLock(String key) {
        return baseLockMethod.getLock(key);
    }

    /**
     * 获取锁，如果锁被使用中，会自动等待锁释放后执行
     *
     * @param key      锁的key
     * @param runnable 执行的方法
     */
    public void lock(String key, Runnable runnable) {
        baseLockMethod.lock(key);
        try {
            runnable.run();
        } finally {
            baseLockMethod.release(key);
        }
    }

    /**
     * 获取统一值
     *
     * @param key      锁key
     * @param supplier 获取值的方法
     * @param <T>      值的类型
     * @return 值
     */
    @SuppressWarnings("all")
    public <T> T lock(String key, Supplier<T> supplier) {
        baseLockMethod.lock(key);
        try {
            if (VALUE_MAP.containsKey(key)) {
                return (T) VALUE_MAP.get(key);
            }
            var value = supplier.get();
            VALUE_MAP.put(key, value);
            return value;
        } finally {
            baseLockMethod.release(key);
        }
    }

    /**
     * 获取锁
     *
     * @param key      锁的key
     * @param runnable 执行的方法
     */
    public void tryLock(String key, Runnable runnable) {
        tryLock(key, runnable, null);
    }

    /**
     * 获取锁
     *
     * @param key           锁的key
     * @param runnable      执行的方法
     * @param errorRunnable 没有获取到锁的方法
     */
    public void tryLock(String key, Runnable runnable, Runnable errorRunnable) {
        if (baseLockMethod.tryLock(key)) {
            try {
                runnable.run();
                return;
            } finally {
                baseLockMethod.release(key);
            }
        }
        if (errorRunnable != null) {
            errorRunnable.run();
        }
    }

    /**
     * 获取锁
     *
     * @param key      锁的key
     * @param supplier 执行的方法
     * @param message  没有获取到值的方法
     */
    public <T> T tryLock(String key, Supplier<T> supplier, String message) {
        if (baseLockMethod.tryLock(key)) {
            try {
                return supplier.get();
            } finally {
                baseLockMethod.release(key);
            }
        }
        throw new BusinessSimpleException(message);
    }

    /**
     * 获取锁
     *
     * @param key      锁的key
     * @param duration 等待时间
     * @param runnable 执行的方法
     */
    public void tryLock(String key, Duration duration, Runnable runnable) {
        tryLock(key, duration, runnable, null);
    }

    /**
     * 获取锁
     *
     * @param key      锁的key
     * @param duration 等待时间
     * @param supplier 执行的方法
     * @param message  没有获取到值的方法
     */
    public <T> T tryLock(String key, Duration duration, Supplier<T> supplier, String message) {
        if (
                duration == null
                        ? baseLockMethod.tryLock(key)
                        : baseLockMethod.tryLock(key, duration)
        ) {
            try {
                return supplier.get();
            } finally {
                baseLockMethod.release(key);
            }
        }
        throw new BusinessSimpleException(message);
    }

    /**
     * 获取锁
     *
     * @param key           锁的key
     * @param duration      等待时间
     * @param runnable      执行的方法
     * @param errorRunnable 没有获取到锁的方法
     */
    public void tryLock(String key, Duration duration, Runnable runnable, Runnable errorRunnable) {
        if (
                duration == null
                        ? baseLockMethod.tryLock(key)
                        : baseLockMethod.tryLock(key, duration)
        ) {
            try {
                runnable.run();
                return;
            } finally {
                baseLockMethod.release(key);
            }
        }
        if (errorRunnable != null) {
            errorRunnable.run();
        }
    }

    /**
     * 释放锁
     *
     * @param key 锁的key
     */
    public void release(String key) {
        baseLockMethod.lock(key);
    }

}
